#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "parse_code.h"
#include "map.h"
#include "symbol.h" 
#include "base_func.h" 

typedef struct var_list_tag
{
    Map *var;
    struct var_list_tag *next;
} VarList;

int VM(Byte *bp, Word cur, VarList *var_list);
VarList * GetVarListMemory();

VarList *variable_list;
//解析字节码,使用的栈
static Value stack[256];
static Value *top=stack, *base=stack; //栈顶, 栈底
int usr_stack_num = 0;
int initVM(Byte *bp, Word cur){
    //for(int i=0; i<256; i++) stack[i].entity = GetEntityMemory();
    function_list = SetSystemFunction();  
    variable_list = GetVarListMemory();
    global_variable = CreateMap();
    VM(bp, cur, variable_list);
}

// is_exit 是否向上查找
Value * GetMapValue(VarList *var_list, char * key, int is_exit){
    if(!is_exit) return GetValue(var_list->var, key);

    VarList *p;
    for( p = var_list; p; p=p->next){
        Value * val = GetValue(p->var, key);
        if(val) return val;
    }

    printf("Can't find variable: %s\n", key);
    exit(0);
    
    
}
VarList * GetVarListMemory(){
    VarList * list = (VarList*)malloc(sizeof(VarList));
    list->var = CreateMap();
    return list;
}
int VM(Byte *bp, Word cur, VarList *var_list){
    Byte *sp = bp;
    if(CODE_DEBUG){ printf("-----VM------\n"); };
    while( sp < (bp+cur) ) {
        switch ((OpCode)*sp++) {
            case PUSH_NULL:
                if(CODE_DEBUG){ printf("PUSH_NULL\n"); };
                break;
            case PUSH_CREATE_NUM: {
                if(CODE_DEBUG){ printf("PUSH_CREATE_NUM\n"); };
                
                Word indx;
                get_index(indx, sp); 
                char * name = get_name_value(indx); //堆内存

                Value *var = GetMapValue(var_list, name, 0);
                if(var) {
                    printf("repeat create variable: %s\n", name);
                    return 0;
                }
                

                Value *var_name = GetValueMemory();
                var_name->name = strdup(name);
                var_name->type = DATA_TYPE_NUMBER;
                
                top->name = var_name->name;
                top->type = var_name->type;
                assignment(top, var_name, 1);

                SetValue(var_list->var, var_name, name);
                top->entity= var_name->entity;
                put_stcak_cursor(top, usr_stack_num);
                break;
            }
            case PUSH_CREATE_STR:{
                if(CODE_DEBUG){ printf("PUSH_CREATE_STR\n"); };
                Word indx;
                get_index(indx, sp); 
                char * name = get_name_value(indx); //堆内存

                // Value *var = GetValue(global_variable, name);
                Value *var = GetMapValue(var_list, name, 0);
                if(var) {  
                    printf("repeat create variable: %s\n", name);
                    return 0;
                }

                Value *var_name =GetValueMemory();
                var_name->name = strdup(name);
                var_name->type = DATA_TYPE_STRING;

                top->name = var_name->name;
                top->type = var_name->type;
                assignment(top, var_name, 1);

                SetValue(var_list->var, var_name, name);
                top->entity= var_name->entity;
                put_stcak_cursor(top, usr_stack_num);
                break;
            }
            case PUSH_CREATE_FUNC:
                if(CODE_DEBUG){ printf("PUSH_CREATE_FUNC\n"); };
                break;
            case PUSH_CREATE_BOOL:
                if(CODE_DEBUG){ printf("PUSH_CREATE_BOOL\n"); };
                break;
            case PUSH_ARR_DEF:{
                if(CODE_DEBUG){ printf("PUSH_ARR_DEF\n"); };
                Word count;
                get_index(count, sp);
                count *= sizeof(Word);
                sp += count;
                break;
            }
            case PUSH_CREATE_ARRAY:{
                if(CODE_DEBUG){ printf("PUSH_CREATE_ARRAY\n"); };
                Word layer;
                get_index(layer, sp);
                Word layer_count[layer];
                Value *tmp = top-1-layer;                
                for(int i=0; i<layer; i++){
                    if( (tmp+1+i)->type != DATA_TYPE_NUMBER) {} //TODO 类型不等于数字或数字小于0 
                    layer_count[i] = (Word)(tmp+1+i)->entity->number;
                }

                
                //定位到声明数组的变量
                Value * tmp_var =  GetMapValue(var_list, tmp->name, 1);
                createArray(0, layer-1, tmp_var, layer_count); 
                tmp->type = tmp_var->type;
                tmp->entity = tmp_var->entity;
                tmp->attr = tmp_var->attr;
                tmp->length = tmp_var->length;
                tmp->array = tmp_var->array;
                top -= layer;
                // printf("length:%hd\n",(top-1)->length);
                // printf("length:%hd\n",(top-1)->array[0].length);
                // printf("length:%hd\n",(top-1)->array[1].length);
                
                break;
            }
            case PUSH_ARRAY:{
                if(CODE_DEBUG){ printf("PUSH_ARRAY\n"); };
                Word layer;
                get_index(layer, sp);
                if(layer < 1){} // TODO 层数错误
                
                Word layer_count[layer];
                Value *arr = top-1-layer;
                for(int i=0; i<layer; i++){
                    if( (arr+1+i)->type != DATA_TYPE_NUMBER) {
                        char e[255];
                        sprintf(e, "Array: %s index type \n", arr->name);
                        console_error(e);
                    }
                    layer_count[i] = (Word)(arr+1+i)->entity->number;
                }

                short is_data=-1;
                for(Word i=0; i<layer; i++){
                    if(layer_count[i] > arr->length ){
                        char e[255];
                        sprintf(e, "Array: %s length \n", arr->name);
                        console_error(e);
                    }
                    if(arr->attr == ATTR_ARRAY ){
                        arr = &(arr->array[ layer_count[i] ]);
                    }else if(arr->attr == ATTR_ARRAY_DATA){
                        is_data = layer_count[i];
                    }
                }

                top -= layer;
                assignment(top-1, arr , 1);
                if(is_data != -1){
                    (top-1)->entity = (top-1)->entity+is_data;
                }
                
                // printf("arr->entity[2]:%.0f \n", arr->entity[2].number);
                // if((top-1)->attr == ATTR_ARRAY_DATA) printf("arr->length:%hd \n", (top-1)->length);
                break;
            }
            case PUSH_ARRAY_SET:{
                if(CODE_DEBUG){ printf("PUSH_ARRAY_SET\n"); };
                
                pop_stcak_cursor(top, usr_stack_num);
                int num=0, rise=0;
                while(1){
                    if(is_array(top)) break;
                    num++;
                    pop_stcak_cursor(top, usr_stack_num);
                }
                // printf("length: %hd \n", top->length);
                
                if(num > top->length){
                    char e[255];
                    sprintf(e, "Array: %s Lack of space \n", top->name);
                    console_error(e);
                }
                
                while(rise < num){
                    //检测类型
                    if(top->type != (top+rise)->type){
                        char e[255];
                        sprintf(e, ":Array: %s Different types prohibit assign \n", top->name);
                        console_error(e);
                    }
                    EntityAssignment(&(top->entity[rise]),  (top+rise+1)->entity, top->type);
                    rise++;
                }

                // for(;num>0;num--){
                //     printf("top->entity[%d]=%.0f \n", num-1, top->entity[num-1].number);
                // }
                put_stcak_cursor(top, usr_stack_num);
                break;
            }
            case PUSH_NAME:{
                if(CODE_DEBUG){ printf("PUSH_NAME\n"); };
                Word indx;
                get_index(indx, sp); 

                char * name = get_name_value(indx); //堆内存
                Value * tmp_var;
                //把值找出来压栈
                if( (OpCode)*sp == PUSH_FUNC_TAG){ // 函数
                    tmp_var = GetValue(function_list, name);
                    if(!tmp_var){
                        char e[255];
                        sprintf(e, "Can't find function: %s\n", name);
                        console_error(e);
                    }
                }else{ //变量
                    // tmp_var = GetValue(global_variable, name);
                    tmp_var = GetMapValue(var_list, name, 1);
                }

                top->name = tmp_var->name;
                top->type = tmp_var->type;
                assignment(top, tmp_var, 1);

                put_stcak_cursor(top, usr_stack_num);
                break;
            }
            case PUSH_STRING:{
                if(CODE_DEBUG){ printf("PUSH_STRING\n"); };
                Word indx;
                get_index(indx, sp); 
                char * string = get_constant_value(indx); //堆内存
                top->type = DATA_TYPE_STRING;
                top->entity = GetEntityMemory();
                top->entity->string = string;
                top->attr = ATTR_CONSTANT;
                top->length = 1;
                put_stcak_cursor(top, usr_stack_num);
                break;
            }
            case PUSH_NUMBER:{
                if(CODE_DEBUG){ printf("PUSH_NUMBER\n"); };
                top->type = DATA_TYPE_NUMBER;
                top->entity = GetEntityMemory();
                top->entity->number =  *(double *)sp;
                top->attr = ATTR_CONSTANT;
                top->length = 1;
                sp += sizeof(double);
                put_stcak_cursor(top, usr_stack_num);
                break;
            }
            case PUSH_ASSIGN:{
                if(CODE_DEBUG){ printf("PUSH_ASSIGN\n"); };
                // BUG: f[0]=e, f[0]下面并没有e的值
                Value *key = top-2; //f[0]
                Value *val = top-1; //e
                if(key->type != val->type){
                    printf("Different types prohibit assign! \n");
                    return 0;
                }
                
                Value *var = GetMapValue(var_list, key->name, 1);
                assignment(key, val, 0);
                // printf("f[0][0]:%.0f \n", var->array[0].entity[0].number);
                // printf("f[0][1]:%.0f \n", var->array[0].entity[1].number);
                // printf("f[0][2]:%.0f \n", var->array[0].entity[2].number);

                pop_stcak_cursor(top, usr_stack_num);
                break;
            }
                
            case PUSH_ADD:{
                if(CODE_DEBUG){ printf("PUSH_ADD\n"); };
                Value *val1 = top-2;
                Value *val2 = top-1;
                if(val1->type != val2->type){
                    printf("no num type Can't calculate! \n");
                    return 0;
                }
                val1->entity->number += val2->entity->number;
                pop_stcak_cursor(top, usr_stack_num);
                break;
            }
            case PUSH_SUB:{
                if(CODE_DEBUG){ printf("PUSH_SUB\n"); };
                Value *val1 = top-2;
                Value *val2 = top-1;
                if(val1->type != val2->type){
                    printf("no num type Can't calculate! \n");
                    return 0;
                }
                val1->entity->number -= val2->entity->number;
                pop_stcak_cursor(top, usr_stack_num);
                break;
            }
            case PUSH_MUL:{
                if(CODE_DEBUG){ printf("PUSH_MUL\n"); };
                Value *val1 = top-2;
                Value *val2 = top-1;
                if(val1->type != val2->type){
                    printf("no num type Can't calculate! \n");
                    return 0;
                }
                val1->entity->number *= val2->entity->number;
                pop_stcak_cursor(top, usr_stack_num);
                break;
            }
            case PUSH_DIV:{
                if(CODE_DEBUG){ printf("PUSH_DIV\n"); };
                Value *val1 = top-2;
                Value *val2 = top-1;
                if(val1->type != val2->type){
                    printf("no num type Can't calculate! \n");
                    return 0;
                }
                val1->entity->number /= val2->entity->number;
                pop_stcak_cursor(top, usr_stack_num);
                break;
            }
            case PUSH_FUNC_TAG:
                if(CODE_DEBUG){ printf("PUSH_FUNC_TAG\n"); };
                top->type = STACK_TAG;
                put_stcak_cursor(top, usr_stack_num);
                break;
            case PUSH_FUNC_CALL:{
                if(CODE_DEBUG){ printf("PUSH_FUNC_CALL\n"); };
                Value * f = top;
                f--;
                int param_count = 0;
                while(1){
                    if(f->type == STACK_TAG) break;
                    param_count++;
                    f--;
                }
                f--; //定位到func name stcak
                if(f->type == DATA_TYPE_SYSTEM_FUNC){
                    f->entity->system_func(param_count, f+2);
                    //清栈
                    top -= param_count+2;
                    usr_stack_num -= param_count+2;
                }else if(f->type == DATA_TYPE_FUNC){
                    VarList * tmp_list = GetVarListMemory();
                    tmp_list->next = var_list;
                    //printf("function name:%s \n", f->name);
                    VM(f->entity->user_func->content, f->entity->user_func->content_length, tmp_list);
                    // 释放内存
                    MapFree(tmp_list->var);
                    free(tmp_list);
                }
                break;
            }
            case PUSH_REMOVE_STACK:{
                OpCode  ret_type=(OpCode)*(bp+cur-2);
                // 定义了返回类型却没返回值
                if(ret_type != RET_NULL && sp == bp+cur ){
                    printf("the function want return value! \n");
                    return 0;
                }
                // 定义了返回类型 && 有return语句
                // 需要检测类型是否一致
                int pop_stcak_count = 1;
                Value * ret_data = --top;
                if(ret_type != RET_NULL && sp != bp+cur ){
                    if( codeTypeTodataType(ret_type) != ret_data->type){
                        printf("return value type error! \n");
                        return 0;
                    }
                }

                //清栈
                while(1){
                    if(top->type == STACK_TAG) break;
                    pop_stcak_count++;
                    top--;
                }
                if(ret_type != RET_NULL) {
                    (top-1)->type = ret_data->type;
                    (top-1)->name = ret_data->name;
                    assignment((top-1), ret_data, 0);
                }else{
                    // 定位到函数调用的 name stack
                    pop_stcak_count++;
                    top--;
                }
                usr_stack_num -= pop_stcak_count;
                // 结束
                sp += cur;
                break;
            }
            case RET_NULL: break;
            case RET_ARR_NUM: break;
            case RET_ARR_STR: break;
            case RET_ARR_BOOL: break;
            case RET_NUM: break;
            case RET_STR: break;
            case RET_BOOL: break;
            case PUSH_RESET_ARGV:{//重置参数
                if(CODE_DEBUG){ printf("PUSH_RESET_ARGV\n"); };
                Byte param_count = *sp++;
                if((top-(param_count*2+1))->type != STACK_TAG ){
                    printf("parameter error! \n");
                    return 0;
                }
                // 形参&实参 逐一赋值
                for(Byte i=param_count; i>0; i--){
                    if( (top-i)->type != (top-i-param_count)->type){
                        char e[255];
                        sprintf(e, "params %d type error! \n", param_count-i+1);
                        console_error(e);
                    }
                    // Value * param= GetValue(global_variable, (top-i)->name);
                    Value * param= GetMapValue(var_list, (top-i)->name, 1);
                    assignment(param, (top-i-param_count) , 0);
                }
                //清栈
                top -= param_count*2;
                usr_stack_num -= param_count*2;
                break;
            }
            case PUSH_DEF_FUNC:{
                if(CODE_DEBUG){ printf("PUSH_DEF_FUNC\n"); };

                Word indx;
                get_index(indx, sp);
                char * name = get_name_value(indx);

                Word content_length;
                get_index(content_length, sp);

                SetValue(function_list, SetUserFunction(name, (sp+sizeof(Word)+1), content_length), name);
                break;
            }
            case PUSH_COMPARISON_EQ:{
                if(CODE_DEBUG){ printf("PUSH_COMPARISON_EQ\n"); };
                Value *val1 = top-1;
                Value *val2 = top-2;
                if(val1->type != val2->type || (val2->type!=DATA_TYPE_BOOLEAN&&val2->type!=DATA_TYPE_NUMBER&&val2->type!=DATA_TYPE_STRING) ){
                    printf("Inconsistent type, Can't compare! \n");
                    return 0;
                }

                switch (val2->type)
                {
                    case DATA_TYPE_BOOLEAN: val2->type = boolean_eq(val2,val1) ? val2->type : DATA_TYPE_NULL; break;
                    case DATA_TYPE_NUMBER: val2->type = number_eq(val2,val1) ? val2->type : DATA_TYPE_NULL; break;
                    case DATA_TYPE_STRING: val2->type = string_eq(val2,val1) ? val2->type : DATA_TYPE_NULL; break;
                }

                pop_stcak_cursor(top, usr_stack_num);
                break;
            }
            case PUSH_COMPARISON_NEQ:{
                if(CODE_DEBUG){ printf("PUSH_COMPARISON_NEQ\n"); };

                break;
            }
            case PUSH_COMPARISON_GT:{
                if(CODE_DEBUG){ printf("PUSH_COMPARISON_GT\n"); };
                Value *val1 = top-1;
                Value *val2 = top-2;
                if(val1->type != val2->type || (val2->type!=DATA_TYPE_BOOLEAN&&val2->type!=DATA_TYPE_NUMBER&&val2->type!=DATA_TYPE_STRING) ){
                    printf("Inconsistent type, Can't compare! \n");
                    return 0;
                }
                switch (val2->type)
                {
                    case DATA_TYPE_BOOLEAN: val2->type = boolean_gt(val2,val1) ? val2->type : DATA_TYPE_NULL; break;
                    case DATA_TYPE_NUMBER: val2->type = number_gt(val2,val1) ? val2->type : DATA_TYPE_NULL; break;
                    case DATA_TYPE_STRING: val2->type = string_gt(val2,val1) ? val2->type : DATA_TYPE_NULL; break;
                }
                pop_stcak_cursor(top, usr_stack_num);
                break;
            }
            case PUSH_COMPARISON_LT:{
                if(CODE_DEBUG){ printf("PUSH_COMPARISON_LT\n"); };
                Value *val1 = top-1;
                Value *val2 = top-2;

                if(val1->type != val2->type || (val2->type!=DATA_TYPE_BOOLEAN&&val2->type!=DATA_TYPE_NUMBER&&val2->type!=DATA_TYPE_STRING) ){
                    printf("Inconsistent type, Can't compare! \n");
                    return 0;
                }
                switch (val2->type)
                {
                    case DATA_TYPE_BOOLEAN: val2->type = boolean_lt(val2,val1) ? val2->type : DATA_TYPE_NULL; break;
                    case DATA_TYPE_NUMBER: val2->type = number_lt(val2,val1) ? val2->type : DATA_TYPE_NULL; break;
                    case DATA_TYPE_STRING: val2->type = string_lt(val2,val1) ? val2->type : DATA_TYPE_NULL; break;
                }
                pop_stcak_cursor(top, usr_stack_num);
                break;
            }
            case PUSH_COMPARISON_GE:{
                if(CODE_DEBUG){ printf("PUSH_COMPARISON_GE\n"); };
                Value *val1 = top-1;
                Value *val2 = top-2;

                if(val1->type != val2->type || (val2->type!=DATA_TYPE_BOOLEAN&&val2->type!=DATA_TYPE_NUMBER&&val2->type!=DATA_TYPE_STRING) ){
                    printf("Inconsistent type, Can't compare! \n");
                    return 0;
                }
                switch (val2->type)
                {
                    case DATA_TYPE_BOOLEAN: val2->type = boolean_ge(val2,val1) ? val2->type : DATA_TYPE_NULL; break;
                    case DATA_TYPE_NUMBER: val2->type = number_ge(val2,val1) ? val2->type : DATA_TYPE_NULL; break;
                    case DATA_TYPE_STRING: val2->type = string_ge(val2,val1) ? val2->type : DATA_TYPE_NULL; break;
                }
                pop_stcak_cursor(top, usr_stack_num);
                break;
            }
            case PUSH_COMPARISON_LE:{
                if(CODE_DEBUG){ printf("PUSH_COMPARISON_LE\n"); };

                break;
            }
            case PUSH_COMPARISON_AND:{
                if(CODE_DEBUG){ printf("PUSH_COMPARISON_AND\n"); };
                Value *val1 = top-1;
                Word cur;
                get_index(cur, sp);
                if( P_empty(val1) ) {
                    sp +=cur;
                    pop_stcak_cursor(top, usr_stack_num);
                }
                break;
            }
            case PUSH_COMPARISON_OR:{
                if(CODE_DEBUG){ printf("PUSH_COMPARISON_OR\n"); };
                Value *val1 = top-1;
                Word cur;
                get_index(cur, sp);
                if(!P_empty(val1)) {
                    sp +=cur;
                    pop_stcak_cursor(top, usr_stack_num);
                }
                break;
            }
            case PUSH_NOP:{
                if(CODE_DEBUG){ printf("PUSH_NOP\n"); };
                Word cur;
                get_index(cur, sp);
                break;
            }
            case PUSH_IF_JMP:{
                if(CODE_DEBUG){ printf("PUSH_IF_JMP\n"); };
                Word cur;
                get_index(cur, sp);
                Value *val1 = top-1;
                if( P_empty(val1) ) {
                    sp +=cur;
                }
                pop_stcak_cursor(top, usr_stack_num);
                break;
            }
            case PUSH_JMP:{
                if(CODE_DEBUG){ printf("PUSH_JMP\n"); };
                Word cur;
                get_index(cur, sp);
                sp +=cur;
                break;
            }
            case PUSH_SUB_JMP:{
                if(CODE_DEBUG){ printf("PUSH_SUB_JMP\n"); };
                Word cur;
                get_index(cur, sp);
                sp -=cur;
                break;
            }
            case FILE_OVER: 
                if(CODE_DEBUG){ printf("FILE_OVER\n"); };
                break;
                //return 0;
            default:
                printf("internal error - opcode didn't match \n");
                return 1;
        }
    
    }
}